/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_orm.htm
 *
 * Zhiqim Orm is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.orm.dbo.defined;

import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import org.zhiqim.kernel.model.maps.LinkedMapSV;
import org.zhiqim.kernel.model.maps.MapSS;
import org.zhiqim.kernel.util.Arrays;
import org.zhiqim.kernel.util.Classes;
import org.zhiqim.kernel.util.Lists;
import org.zhiqim.kernel.util.Validates;
import org.zhiqim.orm.ORMConstants;
import org.zhiqim.orm.ORMType;
import org.zhiqim.orm.dbo.Condition;
import org.zhiqim.orm.dbo.Dbo;
import org.zhiqim.orm.dbo.Selector;
import org.zhiqim.orm.dbo.Updater;
import org.zhiqim.orm.dbo.UpdaterField;
import org.zhiqim.orm.dbo.condition._Compare;
import org.zhiqim.orm.dbo.condition._In;
import org.zhiqim.orm.dbo.condition._InNot;
import org.zhiqim.orm.dbo.condition._IsNotNull;
import org.zhiqim.orm.dbo.condition._IsNull;
import org.zhiqim.orm.dbo.condition._Or;

/**
 * 标准数据库对象，配置格式如：<br><br>
 * <table name="com.zhiqim.dbo.User" table="USER" key="USER_ID"><br>
 *     <field name="userId" column="USER_ID" type="long" style="bigint" notNull="true"/><br>
 *     <field name="userName" column="USER_NAME" type="String" style="varchar(32)" notNull="true"/><br>
 * </table><br><br>
 * 用于ZTable时，传入_Table类，映射成数据库表，和根据数据库表列组装成_Table对象
 *
 * 注：_Table支持通过column找到_TableField
 * 
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class _Table implements Dbo, ORMConstants 
{
    private String name;        //类名
    private String table;       //表名
    private String key;         //主键
    private String type;        //表类型
    
    private List<_TableIndex> indexList = new ArrayList<>();
    private List<_TableReplace> replaceList = new ArrayList<>();
    private LinkedMapSV<_TableField> fieldMap = new LinkedMapSV<>();
    private LinkedMapSV<_TableField> columnMap = new LinkedMapSV<>();
    
    /**
     * 给非实体类创建_Table调用，默认name=table，默认type=""
     * 
     * @param table     表名
     * @param key       主键，多个逗号隔开
     */
    public _Table(String table, String key)
    {
        this(table, table, key, _EMPTY_);
    }
    
    /**
     * 给非实体类创建_Table调用，默认name=table
     * 
     * @param table     表名
     * @param key       主键，多个逗号隔开
     * @param type      表类型，如MySQL的InnoDB
     */
    public _Table(String table, String key, String type)
    {
        this(table, table, key, type);
    }
    
    /**
     * 给实体类创建_Table调用，默认name=类名
     * 
     * @param table     表名
     * @param key       主键，多个逗号隔开
     * @param type      表类型，如MySQL的InnoDB
     */
    public _Table(String name, String table, String key, String type)
    {
        this.name = name;
        this.table = table;
        this.type = type;
        
        if (Validates.isNotEmptyBlank(key))
            this.key = key.toUpperCase();
    }
    
    /**
     * 增加索引
     * 
     * @param name      索引名称
     * @param column    索引列，多个逗号隔开
     * @param unique    是否唯一索引
     */
    public void addIndex(String name, String column, boolean unique)
    {
        _TableIndex index = new _TableIndex(name, column.toUpperCase(), unique);
        indexList.add(index);
    }
    
    /**
     * 增加索引，默认索引名=IX_表名
     * 
     * @param name      索引名称
     * @param column    索引列，多个逗号隔开
     * @param unique    是否唯一索引
     */
    public void addIndex(String column, boolean unique)
    {
        _TableIndex index = new _TableIndex("IX_"+table, column.toUpperCase(), unique);
        indexList.add(index);
    }
    
    /**
     * 增加可替换字段
     * 
     * @param replace   可替换字段
     */
    public void addReplace(String name, String column)
    {
        replaceList.add(new _TableReplace(name, column));
    }
    
    /**
     * 增加字段
     * 
     * @param field     字段名
     * @param column    数据表列名
     * @param type      数据类型，格式如int|string,32|string,19,char表示固定长度
     * @param notNull   是否不允许为null
     */
    public void addField(String field, String column, String type, boolean notNull)
    {
        _TableField tField = new _TableField(field, column.toUpperCase(), type, notNull);
        fieldMap.put(field, tField);
        columnMap.put(column.toUpperCase(), tField);
    }
    
    /**
     * 增加字段，默认字段名为首字母小写，遇下划线字母大写
     * 
     * @param column    数据表列名
     * @param type      数据类型，格式如int|string,32|string,19,char表示固定长度
     * @param notNull   是否不允许为null
     */
    public void addField(String column, String type, boolean notNull)
    {
        String field = ORMType.toLowerName(column);
        _TableField tField = new _TableField(field, column.toUpperCase(), type, notNull);
        fieldMap.put(field, tField);
        columnMap.put(column.toUpperCase(), tField);
    }
    
    @Override
    public String toString()
    {
        StringBuilder strb = new StringBuilder(_FOUR_).append("<table")
            .append(" name=").append(_DOUBLE_QUOTE_).append(name).append(_DOUBLE_QUOTE_)
            .append(" table=").append(_DOUBLE_QUOTE_).append(table).append(_DOUBLE_QUOTE_)
            .append(" key=").append(_DOUBLE_QUOTE_).append(key).append(_DOUBLE_QUOTE_)
            .append(" type=").append(_DOUBLE_QUOTE_).append(type).append(_DOUBLE_QUOTE_)
            .append(">").append(_BR_);
        
        for (_TableIndex index : indexList)
        {
            strb.append(_FOUR_).append(_FOUR_).append(index).append(_BR_);
        }
        strb.append(_BR_);
        
        for (_TableField field : fieldMap.values())
        {
            strb.append(_FOUR_).append(_FOUR_).append(field).append(_BR_);
        }
        
        strb.append(_FOUR_).append("</table>");
        return strb.toString();
    }
    
    /***************************************************************************************************/
    //DBO接口实现的方法
    /***************************************************************************************************/
    
    @Override /** 获取类名 */
    public String getName()
    {
        return name;
    }
    
    @Override /** 判断是否存在该字段 */
    public boolean hasField(String fieldName)
    {
        return fieldMap.containsKey(fieldName)?true:columnMap.containsKey(fieldName.toUpperCase());
    }
    
    @Override /** 通过字段名获取字段 */
    public _TableField getField(String fieldName)
    {
        _TableField field = fieldMap.get(fieldName);
        return (field != null)?field:columnMap.get(fieldName.toUpperCase());
    }
    
    @Override /** 通过字段名获取组装GET方法 */
    public String getGetMethod(String fieldName)
    {
        _TableField field = getField(fieldName);
        return (field == null)?null:field.getGetMethod();
    }
    
    @Override /** 根据字段查表列名 */
    public String getColumn(String fieldName)
    {
        _TableField field = getField(fieldName);
        return (field == null)?null:field.getColumn();
    }
    
    @Override /** 获取字段列表按长度排序 */
    public _TableField[] getFieldColumnListOrderByLen()
    {
        _TableField[] array = new _TableField[fieldMap.size()];
        int index = 0;
        for (_TableField field : fieldMap.values())
        {
            array[index++] = field;
        }
        
        for (int i=0;i<array.length;i++)
        {
            for (int j=i+1;j<array.length;j++)
            {
                if (array[i].getField().length() < array[j].getField().length())
                {//冒泡交换
                    _TableField v = array[i];
                    array[i] = array[j];
                    array[j] = v;
                }
            }
        }
        
        return array;
    }
    
    /***************************************************************************************************/
    //_Table私有的方法
    /***************************************************************************************************/
    
    /** 根据名称查字段set方法 */
    public String getSetMethod(String field)
    {
        //先判断是否是字段名,再判断是否是表列名
        _TableField tField = getField(field);
        return (tField == null)?null:tField.getSetMethod();
    }
    
    /** 获取字段列表 */
    public List<_TableField> getFieldList()
    {
        return new ArrayList<_TableField>(fieldMap.values());
    }
    
    public List<_TableField> getFieldListNoKey()
    {
        List<String> keyList = getKeyList();
        List<_TableField> fieldList = new ArrayList<_TableField>();
        for (_TableField field : fieldMap.values())
        {
            if (keyList.contains(field.getColumn()))
                continue;
            fieldList.add(field);
        }
        
        return fieldList;
    }
    
    /** 是否有对应的所有字段名 */
    public boolean hasAllField(String[] fieldArr)
    {
        for (String field : fieldArr)
        {
            if (!fieldMap.containsKey(field))
                return false;
        }
        return true;
    }
    
    /** 验证主键是否正确 */
    public boolean isValidKey()
    {
        if (Validates.isEmptyBlank(key))
            return false;
        
        String[] keyArr = Arrays.toStringArray(key, ",");
        String ck = null;
        for (String k : keyArr)
        {
            if (k.equals(ck))//检查是否有重复
                return false;
            
            if (!columnMap.containsKey(k))//检查是否是列名
                return false;
            
            ck = k;
        }
        
        return true;
    }
    
    /** 验证主键和传参是否类型一致性 */
    public boolean isValidKeyObj(Object[] objArr)
    {
        String[] keyArr = getKeyArr();
        if (keyArr.length != objArr.length)
            return false;//主键数目不相等
        
        int[] typeArr = new int[keyArr.length];
        for (int i=0;i<keyArr.length;i++)
        {
            _TableField field = columnMap.get(keyArr[i]);
            typeArr[i] = field.getType();
        }
        
        //逐个判断类型是否相等，当前一共支持9种类型
        for (int i=0;i<typeArr.length;i++)
        {
            int type = typeArr[i];
            Object obj = objArr[i];
            
            if (!isValidField(type, obj))
                return false;
        }
        
        return true;
    }
    
    /** 验证查询器类型一致性 */
    public String isValidSelector(Selector selector)
    {
        if (selector == null || Validates.isEmpty(selector.getConditionList()))
            return null;
        
        return isValidCondition(selector.getConditionList());
    }
    
    /** 验证更新器类型一致性 */
    public String isValidUpdater(Updater updater)
    {
        if (updater == null || Validates.isEmpty(updater.getConditionList()))
            return null;
        
        String result = isValidCondition(updater.getConditionList());
        if (result != null)
            return result;
        
        for (UpdaterField field : updater.getFieldList().values())
        {
            if (field.getType() == 1)
                continue;
            
            if (!isValidUpdaterValue(field.getField(), field.getValue()))
                return field.getField();
        }
        
        return null;
    }
    
    /** 验证更新的值是否合法 */
    private boolean isValidUpdaterValue(String field, Object value)
    {
        _TableField f = fieldMap.get(field);
        if (f == null)
            return false;
        
        if (!f.isNotNull() && value == null)
            return true;
        
        return isValidField(f.getType(), value);
    }
    
    /** 验证条件参数值类型是否一致 */
    private String isValidCondition(List<Condition> cList)
    {
        for (Condition c : cList)
        {
            if (c instanceof _IsNull || c instanceof _IsNotNull)
                continue;
            
            if (c instanceof _Compare)
            {
                _Compare _c = (_Compare)c;
                if (!isValidCondition(_c.getField(), _c.getValue()))
                    return _c.getField();
            }
            else if (c instanceof _In)
            {
                _In in = (_In)c;
                if (!isValidFieldIn(in.getField(), in.getValue()))
                    return in.getField();
            }
            else if (c instanceof _InNot)
            {
                _InNot in = (_InNot)c;
                if (!isValidFieldIn(in.getField(), in.getValue()))
                    return in.getField();
            }
            else if (c instanceof _Or)
            {
                _Or or = (_Or)c;
                String result = isValidCondition(or.getCondition());
                if (result != null)
                    return result;
            }
        }
        
        return null;
    }
    
    /** 验证条件是否合法 */
    private boolean isValidCondition(String field, Object value)
    {
        _TableField f = fieldMap.get(field);
        return (f == null)?false:isValidField(f.getType(), value);
    }
    
    /** 是否是对象 */
    private boolean isValidField(int type, Object value)
    {
        switch (type)
        {
        //基本类型
        case Z_ORM_BOOLEAN_INT:return (value instanceof Boolean);
        case Z_ORM_BYTE_INT:
        case Z_ORM_SHORT_INT:
        case Z_ORM_INT_INT:
        case Z_ORM_LONG_INT:return value instanceof Integer || value instanceof Long;
        //小数和时间
        case Z_ORM_DATETIME_INT:return (value instanceof Timestamp);
        case Z_ORM_DECIMAL_INT:return (value instanceof Double);
        //字符串和二进制
        case Z_ORM_STRING_INT:return (value instanceof String);
        case Z_ORM_BINARY_INT:return (value instanceof byte[]);
        default:return false;
        }
    }
    
    /** 是否是数组 */
    private boolean isValidFieldIn(String field, Object value)
    {
        _TableField f = fieldMap.get(field);
        if (f == null)
            return false;
        
        switch (f.getType())
        {
        //基本类型
        case Z_ORM_BYTE_INT:
        case Z_ORM_SHORT_INT:
        case Z_ORM_INT_INT:
        case Z_ORM_LONG_INT:return value instanceof int[] || value instanceof Integer[] || value instanceof long[] || value instanceof Long[];
        //小数和时间
        case Z_ORM_DECIMAL_INT:return (value instanceof double[] || value instanceof Double[]);
        //字符串和二进制
        case Z_ORM_STRING_INT:return (value instanceof String[]);
        default:return true;
        }
    }

    public String getTable()
    {
        return table;
    }
    
    public String getType()
    {
        return type;
    }
    
    public String getKey()
    {
        return key;
    }
    
    public String[] getKeyArr()
    {
        return Arrays.toStringArray(key, ",");
    }
    
    public String[] getKeyFieldArr()
    {
        String[] keys = getKeyArr();
        if (keys.length == 0)
            return keys;
        
        String[] fields = new String[keys.length];
        for (int i=0;i<fields.length;i++)
        {//key都是大写的列名，所以直接读取即可
            fields[i] = columnMap.get(keys[i]).getField();
        }
        return fields;
    }
    
    public List<String> getKeyList()
    {
        return Lists.toStringList(key, ",");
    }
    
    public boolean isAllKey()
    {
        return Arrays.toStringArray(key, ",").length == fieldMap.size();
    }
    
    public List<_TableIndex> getIndexList()
    {
        return indexList;
    }
    
    public void chkReplace(MapSS replaceMap)
    {
        if (replaceList.isEmpty() || replaceMap == null)
            return;
        
        if (!replaceMap.isEmpty())
        {
            //1.找出需要替换field为column的可替换字段
            List<_TableReplace> list = new ArrayList<>();
            for (String field : replaceMap.keySet())
            {
                _TableReplace replace = getReplaceColumn(field);
                if (replace != null)
                {
                    list.add(replace);
                }
            }
            
            //2.替换可替换字段到替换表中
            for (_TableReplace replace : list)
            {//把id换成ID
                String value = replaceMap.remove(replace.getField());
                replaceMap.put(replace.getColumn(), value==null?_EMPTY_:value);
            }
        }
        
        //3.最后检查把未指定的替换字段为空字符串
        for (_TableReplace replace : replaceList)
        {
            if (replaceMap.containsKey(replace.getColumn()))
                continue;
            
            replaceMap.put(replace.getColumn(), _EMPTY_);
        }
    }
    
    public void addReplaceToMap(Object data, MapSS replaceMap)
    {
        if (replaceList.isEmpty() || replaceMap == null)
            return;
        
        for (_TableReplace replace : replaceList)
        {
            Object val = Classes.getFieldValue(data, replace.getField());
            replaceMap.put(replace.getColumn(), val==null?_EMPTY_:String.valueOf(val));
        }
    }
    
    public void addReplaceToData(Object data, MapSS replaceMap)
    {
        if (replaceList.isEmpty() || replaceMap == null || replaceMap.isEmpty())
            return;
        
        for (_TableReplace replace : replaceList)
        {
            String value = replaceMap.get(replace.getColumn());
            if (value != null)
            {
                Field field = Classes.getFieldDeep(data.getClass(), replace.getField());
                if (field != null)
                {
                    Classes.setFieldValue(data, field, value);
                }
            }
        }
    }
    
    private _TableReplace getReplaceColumn(String field)
    {
        for (_TableReplace replace : replaceList)
        {
            if (replace.getField().equals(field))
                return replace;
        }
        
        return null;
    }
}
